/*
 * Copyright (C) 2014 Nathan Templon
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package plugin;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import log.Logger;

/**
 * A class for loading a library stored in a jar file
 * @author Nathan Templon
 */
public class LibraryLoader {
    
    /**
     * The standard constructor for a LibaryLoader - the path to the jar file containing the desired library.
     * @param path the path to the jar file containing the desire library
     * @throws java.io.IOException throws an IOException if there is no jar file at the path specified.
     */
    public LibraryLoader( String path ) throws IOException {
            logger = Logger.getDefaultLogger(); // So that we can add to the program log file.
            
            classes = new ArrayList<>();
            
            logger.log("");
            logger.log("Creating new class loader.");
            logger.log("Attempting to create jar file at \"" + path + ".\"");
            
            // Create a new jar file to use
            this.jar = new JarFile(path);
            
            logger.log("Jar file created successfully.");
            
            this.path = path;
    }
    
    
    /**
     * When called, the LibraryLoader object parses the jar file and loads the contained classes into memory.
     */
    public void loadClasses() {
        Enumeration<?> e = jar.entries(); // Get the entries
        URLClassLoader loader; // Create a loader to read the classes in with
        
        try {
            // Create urls and give them to our class loader
            URL[] urls = { new URL("jar:file:" + path + "!/") };
            loader = URLClassLoader.newInstance(urls);
            
            // Loop through all entries in the jar file
            while (e.hasMoreElements()) {
                JarEntry entry = (JarEntry)e.nextElement();
                
                // Skip all directories and files that are not .class files
                if (entry.isDirectory() || !entry.getName().endsWith(".class")) {
                    continue;
                }
                
                // The (length - 6) accounts for the .class extension
                // Replacing '/' to '.' changes the file paths to class paths
                String className = entry.getName().substring(0, entry.getName().length() - 6);
                className = className.replace('/', '.');
                
                // Load the class and add it to our list of loaded classes
                logger.log("Attempting to load class \"" + className + ".\"");
                classes.add(loader.loadClass(className));
                logger.log("Class \"" + className + "\" was successfully loaded.");
            }
        } 
        // Exception handling - should be more robust
        catch (MalformedURLException | ClassNotFoundException ex) {
            logger.log(ex);
        }
        catch(NullPointerException ex) {
            logger.log(ex);
        }
    }
    
    
    /**
     * A method that returns a class with the specified name from the library
     * @param name the fully qualified class name of the class to be retrieved from the library
     * @return Returns a Class object of the class if it exists in the library, and null if it does not.
     */
    public Class<?> getClass(String name) {
        
        // loop through all classes
        for (Class<?> c : classes) {
            // If the name matches, return the class
            if (name.equals(c.getName())) return c;
        }
        
        // If none of the classes matched, return null
        return null;
    }
    
    private JarFile jar; // The jar file that we are accessing
    private String path; // The path to the jar file that we are accessing
    private Logger logger; // The Logger that will log what all is going on
    private Collection<Class<?>> classes; // The classes loaded from this library
    
}
